package com.yang.service.impl;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonElement;
import com.yang.entity.AudioRecord;
import com.yang.repository.AudioRecordRepository;
import com.yang.thirdparty.aliyun.properties.AliyunProperties;
import com.yang.dto.DigitalHumanTaskDTO;
import com.yang.dto.req.VideoCreationRequest;
import com.yang.entity.DigitalHuman;
import com.yang.entity.VideoTask;
import com.yang.exception.ClientException;
import com.yang.repository.DigitalHumanRepository;
import com.yang.repository.VideoTaskRepository;
import com.yang.service.GenerateVideoService;
import com.yang.type.DigitalHumanActionTypeEnum;
import com.yang.type.DigitalHumanStatusEnum;
import com.yang.type.GenerateVideoActionEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 数字人服务实现类
 * 作者： yang
 * 时间： 2025/07/07
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GenerateVideoServiceImpl implements GenerateVideoService {

    private final AliyunProperties aliModelConfig;
    private final VideoTaskRepository videoTaskRepository;
    private final DigitalHumanRepository digitalHumanRepository;
    private final AudioRecordRepository audioRecordRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DigitalHumanTaskDTO startCreateVideo(VideoCreationRequest params) {
        // 校验图片
        if (!checkImage(params.getImageUrl())) {
            throw new ClientException("上传的图片不符合要求，请上传带有人物面部的图片。");
        }

        // 构建digitalHuman
        DigitalHuman digitalHuman =
                DigitalHuman.builder()
                        .actionType(DigitalHumanActionTypeEnum.GENERATE_VIDEO)
                        // 此task为随机生成，仅用于初始化
                        .taskId(UUID.randomUUID().toString())
                        .status(DigitalHumanStatusEnum.IN_PROGRESS)
                        .inputImageUrl(params.getImageUrl())
                        // 暂时使用默认配置的音色
                        .inputSpeaker(aliModelConfig.getCosyVoice())
                        .inputSpeakText(params.getText())
                        .build();
        digitalHumanRepository.save(digitalHuman);

        // 添加生成音频记录
        AudioRecord audioRecord =
                AudioRecord.builder()
                        .digitalHumanId(digitalHuman.getId())
                        .speakText(digitalHuman.getInputSpeakText())
                        .speaker(digitalHuman.getInputSpeaker())
                        .status(DigitalHumanStatusEnum.IN_PROGRESS)
                        .build();
        audioRecordRepository.save(audioRecord);

        log.info(
                "generateVideo: {}, id: {}, taskId: {}, recordId: {}",
                params.getImageUrl(),
                digitalHuman.getId(),
                digitalHuman.getTaskId(),
                audioRecord);
        return DigitalHumanTaskDTO.builder().taskId(String.valueOf(digitalHuman.getId())).build();
    }

    @Override
    @Transactional
    public void executeVideoGenerationTasks() {
        log.info("开始处理待生成视频任务");

        try {
            // 查询待处理的视频生成任务
            List<VideoTask> pendingTasks = videoTaskRepository.findAll(
                    (root, query, criteriaBuilder) ->
                            criteriaBuilder.equal(root.get("status"), DigitalHumanStatusEnum.IN_PROGRESS));
            log.info("查询到{}个待处理视频任务", pendingTasks.size());

            // 处理每个任务
            for (VideoTask task : pendingTasks) {
                try {
                    processVideoTask(task);
                } catch (Exception e) {
                    log.error("处理视频任务失败，ID: {}", task.getId(), e);
                }
            }

        } catch (Exception e) {
            log.error("执行视频生成定时任务过程中发生错误", e);
        }

        log.info("视频生成任务处理完成");
    }

    /**
     * 查询视频任务状态
     *
     * @param digitalHumanId 任务ID
     * @return 视频访问地址，如果成功
     * @throws ClientException 如果任务失败或不存在
     */
    @Override
    public String queryVideoTaskStatus(Long digitalHumanId) {
        try {
            // 根据任务ID查询任务
            VideoTask task = videoTaskRepository.findTaskByDigitalHumanId(digitalHumanId);
            if(Objects.isNull(task)) {
                throw new ClientException("视频生成任务不存在");
            }
            // 调用API查询任务状态
            String statusResponse = getTaskStatus(task.getTaskId());
            JsonObject statusJson = new Gson().fromJson(statusResponse, JsonObject.class);

            // 检查output对象
            JsonObject output = statusJson.getAsJsonObject("output");
            if (output == null) {
                throw new ClientException("获取任务状态失败，返回数据格式错误");
            }

            // 获取task_status
            String taskStatus = output.get("task_status").getAsString();

            // 根据状态处理
            if ("SUCCEEDED".equals(taskStatus)) {
                // 任务成功，返回视频URL
                JsonObject results = output.getAsJsonObject("results");
                if (results != null && results.has("video_url")) {
                    String videoUrl = results.get("video_url").getAsString();

                    // 更新任务状态为成功
                    task.setStatus(DigitalHumanStatusEnum.DONE);
                    task.setOutputParams(statusResponse);
                    videoTaskRepository.save(task);

                    // 同时更新digitalHuman状态
                    DigitalHuman digitalHuman = digitalHumanRepository.findById(task.getDigitalHumanId()).orElse(null);
                    if (digitalHuman != null) {
                        digitalHuman.setStatus(DigitalHumanStatusEnum.DONE);
                        digitalHuman.setVideoUrl(videoUrl);
                        digitalHumanRepository.save(digitalHuman);
                    }

                    return videoUrl;
                } else {
                    throw new ClientException("视频生成成功但未找到视频地址");
                }
            } else if ("FAILED".equals(taskStatus)) {
                // 任务失败，获取错误信息
                String errorMessage = "视频生成失败";
                if (output.has("message")) {
                    errorMessage = output.get("message").getAsString();
                }

                // 更新任务状态为失败
                task.setStatus(DigitalHumanStatusEnum.FAILED);
                task.setOutputParams(statusResponse);
                task.setFailedCount(task.getFailedCount() + 1);
                videoTaskRepository.save(task);

                // 同时更新digitalHuman状态
                DigitalHuman digitalHuman = digitalHumanRepository.findById(task.getDigitalHumanId()).orElse(null);
                if (digitalHuman != null) {
                    digitalHuman.setStatus(DigitalHumanStatusEnum.FAILED);
                    digitalHumanRepository.save(digitalHuman);
                }

                throw new ClientException(errorMessage);
            } else {
                // 如果状态既不是成功也不是失败，视为处理中
                return "处理中";
            }
        } catch (ClientException e) {
            // 直接抛出业务异常
            throw e;
        } catch (Exception e) {
            log.error("查询视频任务状态失败，任务ID: {}", digitalHumanId, e);
            throw new ClientException("视频生成查询失败: " + e.getMessage());
        }
    }

    /**
     * 处理单个视频任务
     *
     * @param task 视频任务
     */
    private void processVideoTask(VideoTask task) {
        try {

            // 调用LivePortrait API生成视频
            String jsonResponse = sendVideoGenerationRequest(task);

            // 4. 解析响应获取任务ID
            Gson gson = new Gson();
            JsonObject responseJson = gson.fromJson(jsonResponse, JsonObject.class);
            String apiTaskId = responseJson.getAsJsonObject("output").get("task_id").getAsString();

            // 保存任务ID和更新状态为处理中
            task.setTaskId(apiTaskId);
            task.setAction(GenerateVideoActionEnum.CREATE_VIDEO);
            task.setStatus(DigitalHumanStatusEnum.IN_PROGRESS);
            task.setOutputParams(jsonResponse);
            videoTaskRepository.save(task);

            // digitalHuman更新状态为处理中
            DigitalHuman digitalHuman = digitalHumanRepository.findById(task.getDigitalHumanId())
                    .orElseThrow(() -> new ClientException("未找到对应的数字人信息"));
            digitalHuman.setTaskId(apiTaskId);
            digitalHuman.setActionType(DigitalHumanActionTypeEnum.GENERATE_VIDEO);
            digitalHuman.setStatus(DigitalHumanStatusEnum.IN_PROGRESS);
            digitalHumanRepository.save(digitalHuman);

            log.info("视频任务处理成功，ID: {}", task.getId());
        } catch (Exception e) {
            // 更新任务状态为失败
            task.setStatus(DigitalHumanStatusEnum.FAILED);
            task.setFailedCount(task.getFailedCount() + 1);
            videoTaskRepository.save(task);

            log.error("处理视频任务失败，ID: {}", task.getId(), e);
        }
    }

    /**
     * 发送生成视频请求
     *
     * @param task 视频任务
     * @return API响应JSON
     */
    private String sendVideoGenerationRequest(VideoTask task) throws Exception {
        // 解析任务参数
        Map<String, Object> inputParams = new Gson().fromJson(task.getInputParams(), Map.class);

        // 从input子对象中获取image_url和audio_url
        Map<String, String> inputObj = (Map<String, String>) inputParams.get("input");
        if (inputObj == null) {
            throw new Exception("任务参数格式错误，未找到input对象");
        }

        String imageUrl = inputObj.get("image_url");
        String audioUrl = inputObj.get("audio_url");

        if (imageUrl == null || imageUrl.isEmpty()) {
            throw new Exception("任务的图片URL为空");
        }

        if (audioUrl == null || audioUrl.isEmpty()) {
            throw new Exception("任务的音频URL为空");
        }

        log.info("处理视频任务，图片URL: {}, 音频URL: {}", imageUrl, audioUrl);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(aliModelConfig.getLiveApi());

            // 设置请求头
            httpPost.setHeader("X-DashScope-Async", "enable");
            httpPost.setHeader("Authorization", "Bearer " + aliModelConfig.getApiKey());
            httpPost.setHeader("Content-Type", "application/json");

            // 构建请求体
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("model", aliModelConfig.getLiveModel());

            JsonObject input = new JsonObject();
            input.addProperty("image_url", imageUrl);
            input.addProperty("audio_url", audioUrl);
            requestBody.add("input", input);

            JsonObject parameters = new JsonObject();
            parameters.addProperty("template_id", aliModelConfig.getLiveTemplateId());
            parameters.addProperty("eye_move_freq", aliModelConfig.getLiveEyeMoveFreq());
            parameters.addProperty("video_fps", aliModelConfig.getLiveVideoFps());
            parameters.addProperty("mouth_move_strength", aliModelConfig.getLiveMouthMoveStrength());
            parameters.addProperty("paste_back", aliModelConfig.getLivePasteBack());
            parameters.addProperty("head_move_strength", aliModelConfig.getLiveHeadMoveStrength());
            requestBody.add("parameters", parameters);

            // 将发送的信息存入数据
            task.setInputParams(requestBody.toString());
            videoTaskRepository.save(task);

            // 发送请求
            StringEntity entity = new StringEntity(requestBody.toString(), "UTF-8");
            httpPost.setEntity(entity);

            HttpResponse response = httpClient.execute(httpPost);
            return EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            log.error("请求视频生成API时发生I/O错误", e);
            throw new Exception("请求API时发生I/O错误", e);
        }
    }

    /**
     * 校验图片
     *
     * @param imageUrl 图片URL
     * @return true:通过，false:不通过
     */
    private boolean checkImage(String imageUrl) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 构建请求
            HttpPost httpPost = new HttpPost(aliModelConfig.getLiveDetectApi());
            httpPost.addHeader("Content-Type", "application/json");
            httpPost.setHeader("Authorization", "Bearer " + aliModelConfig.getApiKey());

            // 构建请求体
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("model", "liveportrait-detect");

            JsonObject input = new JsonObject();
            input.addProperty("image_url", imageUrl);
            requestBody.add("input", input);

            // 发送请求
            StringEntity entity = new StringEntity(requestBody.toString(), "UTF-8");
            httpPost.setEntity(entity);

            HttpResponse response = httpClient.execute(httpPost);
            String jsonResponse = EntityUtils.toString(response.getEntity());

            // 解析响应
            Gson gson = new Gson();
            JsonObject responseJson = gson.fromJson(jsonResponse, JsonObject.class);
            JsonObject output = responseJson.getAsJsonObject("output");
            if (output != null) {
                JsonElement passElement = output.get("pass");
                if (passElement != null && !passElement.isJsonNull()) {
                    return passElement.getAsBoolean();
                }
            }
            return false;
        } catch (Exception e) {
            log.error("校验图片时发生错误，图片URL: {}", imageUrl, e);
            return false;
        }
    }

    /**
     * 获取任务状态
     *
     * @param taskId 任务ID
     * @return 任务状态响应JSON
     * @throws Exception 如果请求失败
     */
    private String getTaskStatus(String taskId) throws Exception {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(aliModelConfig.getLiveTaskApi() + taskId);
            httpGet.setHeader("Authorization", "Bearer " + aliModelConfig.getApiKey());

            HttpResponse response = httpClient.execute(httpGet);
            return EntityUtils.toString(response.getEntity());
        } catch (IOException e) {
            log.error("获取任务状态时发生错误，任务ID: {}", taskId, e);
            throw new Exception("获取任务状态时发生错误", e);
        }
    }
}
